﻿using System.Collections;
using System.Reflection;
namespace System.Web
{
    internal partial class NetHttpApplicationFactory
    {
        private int _numFreeInstances;
        private Stack _freeInstances = new Stack();
        private Type _applicationType;
        private NetHttpApplicationState _state = new NetHttpApplicationState();
        private bool _appOnStartCalled;

        public NetHttpApplicationFactory(Type applicationType)
        {
            if (applicationType == null)
                throw new ArgumentNullException("applicationType");
            if (!typeof(NetHttpApplication).IsAssignableFrom(applicationType))
                throw new ArgumentOutOfRangeException("httpApplicationType", "must be of type NetHttpApplication");
            _applicationType = applicationType;
            ReflectOnApplicationType();
        }

        #region Init

        private bool _inited;

        private void Init() { }

        private void EnsureInited()
        {
            if (!_inited)
                lock (this)
                    if (!_inited)
                    {
                        Init();
                        _inited = true;
                    }
        }

        #endregion

        #region Reflect Application

        private MethodInfo[] _eventHandlerMethods;

        private void ReflectOnApplicationType()
        {
            ArrayList list = new ArrayList();
            foreach (MethodInfo info in _applicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
                if (ReflectOnMethodInfoIfItLooksLikeEventHandler(info))
                    list.Add(info);
            var baseType = _applicationType.BaseType;
            if ((baseType != null) && (baseType != typeof(NetHttpApplication)))
                foreach (MethodInfo info2 in baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
                    if (info2.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(info2))
                        list.Add(info2);
            _eventHandlerMethods = new MethodInfo[list.Count];
            for (int index = 0; index < _eventHandlerMethods.Length; index++)
                _eventHandlerMethods[index] = (MethodInfo)list[index];
        }

        private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
        {
            ParameterInfo[] parameters;
            string methodName;
            if (m.ReturnType == typeof(void))
            {
                parameters = m.GetParameters();
                switch (parameters.Length)
                {
                    case 0:
                        goto @MethodFoundLabel;
                    case 2:
                        if (!(parameters[0].ParameterType != typeof(object)))
                        {
                            if ((parameters[1].ParameterType != typeof(EventArgs)) && !parameters[1].ParameterType.IsSubclassOf(typeof(EventArgs)))
                                return false;
                            goto @MethodFoundLabel;
                        }
                        return false;
                }
            }
            return false;
        @MethodFoundLabel:
            methodName = m.Name;
            int index = methodName.IndexOf('_');
            if ((index <= 0) || (index > (methodName.Length - 1)))
                return false;
            if (string.Equals(methodName, "Application_OnStart", StringComparison.OrdinalIgnoreCase) || string.Equals(methodName, "Application_Start", StringComparison.OrdinalIgnoreCase))
            {
                _onStartMethod = m;
                _onStartParamCount = parameters.Length;
            }
            else if (string.Equals(methodName, "Application_OnEnd", StringComparison.OrdinalIgnoreCase) || string.Equals(methodName, "Application_End", StringComparison.OrdinalIgnoreCase))
            {
                _onEndMethod = m;
                _onEndParamCount = parameters.Length;
            }
            else if (string.Equals(methodName, "Session_OnEnd", StringComparison.OrdinalIgnoreCase) || string.Equals(methodName, "Session_End", StringComparison.OrdinalIgnoreCase))
            {
                _sessionOnEndMethod = m;
                _sessionOnEndParamCount = parameters.Length;
            }
            return true;
        }

        #endregion

        internal NetHttpApplicationState ApplicationState
        {
            get { return (_state ?? new NetHttpApplicationState()); }
        }

        internal NetHttpApplication GetApplicationInstance(NetHttpWorkerRequest wr, NetHttpContext context)
        {
            EnsureInited();
            EnsureAppStartCalled(context);
            //
            NetHttpApplication application = null;
            lock (_freeInstances)
                if (_numFreeInstances > 0)
                {
                    application = (NetHttpApplication)_freeInstances.Pop();
                    _numFreeInstances--;
                }
            if (application == null)
            {
                application = (NetHttpApplication)NetHttpRuntime.CreateNonPublicInstance(_applicationType, null);
                application.InitInternal(wr, context, _state, this, _eventHandlerMethods);
            }
            return application;
        }

        internal void RecycleApplicationInstance(NetHttpApplication application)
        {
            if (_numFreeInstances < 20)
                lock (_freeInstances)
                {
                    _freeInstances.Push(application);
                    _numFreeInstances++;
                }
        }
    }
}
